--书上例子的简洁版
import Data.Array (Array(..), (!), bounds, elems, indices,ixmap, listArray)

import Control.Applicative ((<$>))
import Control.Monad (forM_)
import Data.Char (digitToInt)
import Data.Ix (Ix(..))
import Data.List (foldl', group, sort, sortBy, tails)
import Data.Maybe (catMaybes, listToMaybe)
import Data.Ratio (Ratio)
import Data.Word (Word8)
import System.Environment (getArgs)
import qualified Data.ByteString.Lazy.Char8 as L
import qualified Data.Map as M


-- ======================================================================================================================
leftOddList = ["0001101", "0011001", "0010011", "0111101", "0100011", "0110001", "0101111", "0111011", "0110111", "0001011"]

complement '0' = '1'
complement '1' = '0'         
--leftOddList 是 char  的双重列表 


--  map (map complement)  leftOddList
rightList = map complement <$> leftOddList
    where complement '0' = '1'
          complement '1' = '0'

leftEvenList = map reverse rightList

parityList = ["111111", "110100", "110010", "110001", "101100", "100110", "100011", "101010", "101001", "100101"]
{-
*Main> rle xs = length xs
*Main> map rle $ group "0011001"
[2,2,2,1]
*Main>
-}
distance xs1 xs2 = sum . fmap abs $  zipWith (-)  xs1   xs2

--distanceStr xs1 xs2 = sum . fmap abs $  zipWith (-) ( fmap digitToInt xs1) ( fmap digitToInt xs2)
toIntList = map length . group
distanceStr xs1 xs2 = distance (toIntList xs1)  (toIntList xs2)


bestScores srl ps = take 3 . sort $ scores
    where scores = zip [distanceStr d ps | d <- srl] digits
          digits = [0..9]  
          
bestScores2 srl ps =  sort $ scores
    where scores = zip [distanceStr d ps | d <- srl] digits
          digits = [0..9]            
-- ***************************************************************************************************
data Parity a = Even a | Odd a | None a
                deriving (Show)

fromParity :: Parity a -> a
fromParity (Even a) = a
fromParity (Odd a) = a
fromParity (None a) = a

parityMap :: (a -> b) -> Parity a -> Parity b
parityMap f (Even a) = Even (f a)
parityMap f (Odd a) = Odd (f a)
parityMap f (None a) = None (f a)

instance Functor Parity where
    fmap = parityMap          
          
on :: (a -> a -> b) -> (c -> a) -> c -> c -> b
on f g x y = g x `f` g y

compareWithoutParity = compare `on` fromParity          
-- ***************************************************************************************************         
--bestLeft :: [Run] -> [Parity (Score, Digit)]
bestLeft ps = sortBy compareWithoutParity
         ((map Odd (bestScores leftOddList ps)) ++
           (map Even (bestScores leftEvenList ps)))

--bestRight :: [Run] -> [Parity (Score, Digit)]
bestRight = map None . bestScores rightList        


--根据某种条件，分割列表；该条件是灵活可变的，可以是数量，可以是数值。             
chunkWith :: ([a] -> ([a], [a])) -> [a] -> [[a]]
chunkWith _ [] = []
chunkWith f xs = let (h, t) = f xs
             in h : chunkWith f t

--chunkWith的特化版，根据数量分割
chunksOf :: Int -> [a] -> [[a]]
chunksOf n = chunkWith (splitAt n)


      
candidateDigits rle | length rle < 95 = []
candidateDigits rle
    | any null match = [[None 3]]
    | otherwise      = map (map (fmap snd)) match
  where match = map bestLeft left ++ map bestRight right
        left = chunksOf 7 . take 42 . drop 3 $ runLengths
        right = chunksOf 7 . take 42 . drop 50 $ runLengths
        runLengths = rle     
-- ============================Run========================Run======================Run===================================================
bitsEnd="101"
bitsBegin="101"
bitsCenter="01010"
--bitsCenter="10101"
bits1R = "1100110"
bits6R = "1010000"
bits1L = "0001101"
bitsRight = bits1R ++ bits1R ++ bits1R ++ bits1R ++ bits1R ++ bits6R
--bitsLeft = bits1L ++ bits1L ++ bits1L ++ bits1L ++ bits1L ++ bits1L
bitsLeft = "0011001" ++ "0011001" ++ "0110011" ++ "0011001" ++ "0110011" ++ "0110011" 

bitsAll = bitsBegin ++ bitsLeft ++ bitsCenter ++ bitsRight ++ bitsEnd
--aa = runLength bitsAll
a1 = "1111111"
a2 = "1111110"
a3 = "1111100"


{-
*Main> bitsAll
"10100110010011001011001100110010110011011001101010110011011001101100110110011011001101010000101"
*Main> candidateDigits  bitsAll
[
[Odd 1,Odd 0,Even 1,Odd 2,Even 2,Even 4],
[Odd 1,Odd 0,Even 1,Odd 2,Even 2,Even 4],
[Even 1,Even 0,Odd 1,Odd 2,Even 2,Odd 4],
[Odd 1,Odd 0,Even 1,Odd 2,Even 2,Even 4],
[Even 1,Even 0,Odd 1,Odd 2,Even 2,Odd 4],
[Even 1,Even 0,Odd 1,Odd 2,Even 2,Odd 4],

[None 1,None 0,None 2],
[None 1,None 0,None 2],
[None 1,None 0,None 2],
[None 1,None 0,None 2],
[None 1,None 0,None 2],
[None 6,None 8,None 2]]

*Main>






-- =============================================================================================================================


checkDigit :: (Integral a) => [a] -> a
checkDigit ds =10 - productSum `mod` 10
    where productSum = sum (mapEveryOther (*3) (reverse ds))

--($)，  (*3) $ 3 = 9  
mapEveryOther :: (a -> a) -> [a] -> [a]
mapEveryOther f = zipWith ($) (cycle [f,id])

get1 xs index 
    | length xs <= index = Nothing
    | otherwise =  last $ take (index + 1) xs
get xs index =  last $ take (index + 1) xs    

encodeEAN13 :: String -> String
encodeEAN13 = concat . encodeDigits . map digitToInt

-- | This function computes the check digit; don't pass one in.
encodeDigits :: [Int] -> [String]
encodeDigits s@(first:rest) =
    outerGuard : lefties ++ centerGuard : righties ++ [outerGuard]
    where (left, right) = splitAt 6 rest
          lefties = zipWith leftEncode (parityList `get` first) left
          righties = map rightEncode (right ++ [checkDigit s])

leftEncode :: Char -> Int -> String
leftEncode '1' = (leftOddList `get`)
leftEncode '0' = (leftEvenList `get`)

rightEncode :: Int -> String
rightEncode = (rightList `get`)

outerGuard = "101"
centerGuard = "01010"

-- =============================================================================================================================
